package com.sdy.usercenter.provider;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.sdy.common.model.BizException;
import com.sdy.common.model.PageQuery;
import com.sdy.common.model.PageResult;
import com.sdy.common.utils.Assert;
import com.sdy.common.utils.MapUtil;
import com.sdy.common.utils.StringUtil;
import com.sdy.mvc.annotation.RemoteService;
import com.sdy.mvc.utils.JsonUtil;
import com.sdy.usercenter.api.UserQueryApi;
import com.sdy.usercenter.api.dto.DeptDto;
import com.sdy.usercenter.api.dto.ResourceDTO;
import com.sdy.usercenter.api.dto.RoleDto;
import com.sdy.usercenter.api.dto.UserDTO;
import com.sdy.usercenter.biz.model.SysDept;
import com.sdy.usercenter.biz.model.SysResource;
import com.sdy.usercenter.biz.model.SysRole;
import com.sdy.usercenter.biz.model.SysRoleResource;
import com.sdy.usercenter.biz.model.SysUser;
import com.sdy.usercenter.biz.model.SysUserDept;
import com.sdy.usercenter.biz.model.SysUserRole;
import com.sdy.usercenter.biz.model.vo.DeptVO;
import com.sdy.usercenter.biz.service.SysDeptService;
import com.sdy.usercenter.biz.service.SysResourceService;
import com.sdy.usercenter.biz.service.SysRoleResourceService;
import com.sdy.usercenter.biz.service.SysRoleService;
import com.sdy.usercenter.biz.service.SysUserAppService;
import com.sdy.usercenter.biz.service.SysUserDeptService;
import com.sdy.usercenter.biz.service.SysUserRoleService;
import com.sdy.usercenter.biz.service.SysUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@RemoteService
public class UserQueryProvider implements UserQueryApi {
    @Autowired
    private SysUserService sysUserService;
    @Autowired
    private SysUserDeptService sysUserDeptService;
    @Autowired
    private SysDeptService sysDeptService;
    @Autowired
    private SysResourceService sysResourceService;
    @Autowired
    private SysRoleService sysRoleService;
    @Autowired
    private SysUserAppService sysUserAppService;
    @Autowired
    private SysUserRoleService sysUserRoleService;
    @Autowired
    private SysRoleResourceService sysRoleResourceService;
    
    private static final String ROLE_CODE_FOR_APPROVE = "#ROLE_CODE_FOR_APPROVE";

    @Override
    public UserDTO getUser(Integer userId) {
        if (userId == null) {
            return null;
        }
        return listUser(Collections.singletonList(userId)).get(0);
    }

    @Override
    public DeptDto getDept(Integer deptId) {
        SysDept dept = sysDeptService.getById(deptId);
        return new DeptDto()
                .setAbbreviation(dept.getAbbreviation())
                .setCode(dept.getCode())
                .setId(dept.getId())
                .setName(dept.getName())
                .setParentId(dept.getParentId());
    }

    private List<SysRole> findRoleByUser(Integer userId, Boolean valid) {
        if (userId == null) {
            return new ArrayList<>();
        }
        List<SysUserRole> rolelist = sysUserRoleService.lambdaQuery().eq(SysUserRole::getUserId, userId).list();
        if (rolelist.isEmpty()) {
            return new ArrayList<>();
        }
        List<Integer> roleIdList = rolelist.stream().map(SysUserRole::getRoleId).collect(Collectors.toList());
        return sysRoleService.lambdaQuery().eq(valid, SysRole::getState, 1).in(SysRole::getId, roleIdList).list();
    }

    @Override
    public List<UserDTO> listUser(List<Integer> userIdList) {
        List<UserDTO> result = new ArrayList<>();
        Collection<SysUser> userList = sysUserService.listByIds(userIdList);
        if (userList.isEmpty()) {
            return result;
        }
        List<SysUserDept> userDeptList = sysUserDeptService.list(
                new LambdaQueryWrapper<SysUserDept>()
                        .in(SysUserDept::getUserId, userIdList)
                        .orderByAsc(SysUserDept::getPrimaryDept)
        );
        Map<Integer, List<SysUserDept>> userDeptMap = MapUtil.collectionToMapList(userDeptList, SysUserDept::getUserId);
        List<SysDept> deptList = sysDeptService.list(
                new LambdaQueryWrapper<SysDept>().in(SysDept::getId, userDeptList.stream().map(SysUserDept::getDeptId).collect(Collectors.toList()))
                        .eq(userDeptList.isEmpty(), SysDept::getId, -1)
        );
        Map<Integer, SysDept> deptMap = MapUtil.collectionToMap(deptList, SysDept::getId);
        userList.forEach(user -> {
            UserDTO dto = new UserDTO();
            dto.setId(user.getId())
                    .setName(user.getName())
                    .setMail(user.getMail())
                    .setCreateTime(user.getCreateTime())
                    .setEffectTime(user.getEffectTime())
                    .setExpireTime(user.getExpireTime())
                    .setGender(user.getGender())
                    .setImgUrl(user.getImgUrl())
                    .setLastLoginIp(user.getLastLoginIp())
                    .setLastLoginTime(user.getLastLoginTime())
                    .setPhone(user.getPhone())
                    .setProfile(user.getProfile())
                    .setState(user.getState())
                    .setType(user.getType())
                    .setUpdateTime(user.getUpdateTime())
                    .setIdCard(user.getIdCard())
                    .setLoginName(user.getLoginName());
            dto.setBManager(0);
            List<SysUserDept> deptL = userDeptMap.get(user.getId());
            if (deptL != null) {
                List<SysUserDept> l1 = deptL.stream().filter(item -> item.getAdmin().equals(2)).collect(Collectors.toList());
                if (l1.size() >= 1) {
                    // 查询所属部门相关信息
                    SysUserDept ownDept = l1.get(0);
                    if (ownDept != null) {
                        SysDept dept = deptMap.get(ownDept.getDeptId());
                        if (dept != null) {
                            dto.setOwnDeptId(ownDept.getDeptId());
                            dto.setOwnDeptName(dept.getAbbreviation());
                        }
                    }
                }
                if (l1.size() >= 2) {
                    // 查询兼职部门相关信息
                    SysUserDept partDept = l1.get(1);
                    if (partDept != null) {
                        SysDept dept = deptMap.get(partDept.getDeptId());
                        if (dept != null) {
                            dto.setPartTimeDeptId(partDept.getDeptId());
                            dto.setPartTimeDeptName(dept.getAbbreviation());
                        }
                    }
                }
                dto.setOwnDeptIdList(l1.stream().map(SysUserDept::getDeptId).collect(Collectors.toList()));

                List<SysUserDept> l2 = deptL.stream().filter(item -> item.getAdmin().equals(1)).collect(Collectors.toList());
                if (l2.size() > 0) {
                    dto.setManageDeptId(l2.get(0).getDeptId());
                }
                dto.setManageDeptIdList(l2.stream().map(SysUserDept::getDeptId).collect(Collectors.toList()));
            } else {
                dto.setOwnDeptIdList(Collections.emptyList());
                dto.setManageDeptIdList(Collections.emptyList());
            }
            List<SysRole> roleList = findRoleByUser(dto.getId(), true);
            if (roleList != null) {
                dto.setRoleIdList(roleList.stream().map(SysRole::getId).collect(Collectors.toList()));
            }
            result.add(dto);
        });
        return result;
    }

    @Override
    public ResourceDTO getAppByCode(String appCode) {
        if (StringUtil.isBlank(appCode)) {
            return null;
        }
        SysResource res = sysResourceService.lambdaQuery().eq(SysResource::getCode, appCode).eq(SysResource::getParentId, 0).one();
        return formatResourceDTO(res);
    }

    @Override
    public List<ResourceDTO> listApp() {
        return sysResourceService.lambdaQuery().eq(SysResource::getParentId, 0).list()
                .stream()
                .map(this::formatResourceDTO)
                .collect(Collectors.toList());
    }

    @Override
    public List<DeptDto> listDept(Integer parentId) {
        List<DeptVO> deptList = sysDeptService.listSubDept(parentId);
        return deptList.stream().map(this::formatDeptDTO).collect(Collectors.toList());
    }

    @Override
    public List<DeptDto> listDept(String keyword) {
        List<DeptVO> deptList = sysDeptService.listDept(keyword);
        return deptList.stream().map(this::formatDeptDTO).collect(Collectors.toList());
    }

    @Override
    public List<DeptDto> listDeptByIds(List<Integer> deptIdList) {
        if (deptIdList == null || deptIdList.size() == 0) {
            return Collections.emptyList();
        }
        Collection<SysDept> deptList = sysDeptService.listByIds(deptIdList);
        return deptList.stream().map(this::formatDeptDTO).collect(Collectors.toList());
    }

    @Override
    public List<RoleDto> listRoleByIds(List<Integer> roleIdList) {
        if (roleIdList == null || roleIdList.size() == 0) {
            return Collections.emptyList();
        }
        Collection<SysRole> l = sysRoleService.listByIds(roleIdList);
        return l.stream().map(this::formatRoleDTO).collect(Collectors.toList());
    }

    @Override
    public List<RoleDto> listRole() {
        List<SysRole> roleList = sysRoleService.lambdaQuery().eq(SysRole::getState, 1).list();
        return roleList.stream().map(this::formatRoleDTO).collect(Collectors.toList());
    }

    @Override
    public List<DeptDto> listUserManageDept(Integer userId, Integer parentId) {
        List<DeptVO> sysDeptList = sysDeptService.listSubDept(parentId);
        return sysDeptList.stream().map(this::formatDeptDTO).collect(Collectors.toList());
    }

    @Override
    public PageResult<UserDTO> pageUserInDept(PageQuery pageQuery, Integer deptId, String username, Boolean recursively) {
        PageResult<SysUser> d = sysUserService.pageUserInDept(pageQuery, deptId, username, recursively);
        PageResult<UserDTO> r = JsonUtil.convertPage2(d, UserDTO.class);
        return r;
    }

    @Override
    public DeptDto getDeptByCode(String deptCode) {
        SysDept dept = sysDeptService.getByCode(deptCode);
        return formatDeptDTO(dept);
    }

    @Override
    public RoleDto getRoleForApprove() throws BizException {
        SysRole role = sysRoleService.lambdaQuery().eq(SysRole::getCode, ROLE_CODE_FOR_APPROVE).one();
        Assert.isTrue(role != null && role.getReadonly().equals(0), "数据异常，请联系管理员");
        if (role == null) {
            role = new SysRole()
                    .setCode(ROLE_CODE_FOR_APPROVE)
                    .setName("组织管理员")
                    .setAdmin(0)
                    .setReadonly(0)
                    .setState(1)
                    .setType(4)
                    .setDescription("审核角色专用")
                    .setCreateTime(new Date());
            sysRoleService.save(role);
        }
        return formatRoleDTO(role);
    }

    @Override
    public void addUserApp(List<Integer> userIdList, String appCode) throws BizException {
        if (!userIdList.isEmpty() && appCode != null) {
            ResourceDTO app = getAppByCode(appCode);
            Assert.isNull(app, "系统不存在：" + appCode);
            sysUserAppService.addUsers(userIdList, app.getId());
        }
    }

    @Override
    public void removeUserApp(List<Integer> userIdList, String appCode) throws BizException {
        if (!userIdList.isEmpty() && appCode != null) {
            ResourceDTO app = getAppByCode(appCode);
            Assert.isNull(app, "系统不存在：" + appCode);
            sysUserAppService.removeUsers(userIdList, app.getId());
        }
    }

    @Override
    public UserDTO getUserByLoginName(String loginName) {
        SysUser sysUser = sysUserService.lambdaQuery().eq(SysUser::getLoginName, loginName).one();
        if (sysUser == null) {
            return null;
        }
        return formatUserDTO(sysUser);
    }

    @Override
    public List<RoleDto> listRoleByAuth(String appCode, String authCode) {
        if (StringUtil.isBlank(authCode) || StringUtil.isBlank(appCode)) {
            return Collections.emptyList();
        }
        SysResource app = sysResourceService.lambdaQuery()
                .eq(SysResource::getCode, appCode)
                .eq(SysResource::getState, 1)
                .eq(SysResource::getType, 1)
                .one();
        if (app == null) {
            return Collections.emptyList();
        }
        SysResource res = sysResourceService.lambdaQuery()
                .eq(SysResource::getRootId, app.getId())
                .eq(SysResource::getCode, authCode)
                .eq(SysResource::getState, 1)
                .one();
        if (res == null) {
            return Collections.emptyList();
        }
        List<Integer> roleIdList = sysRoleResourceService.lambdaQuery()
                .eq(SysRoleResource::getResourceId, res.getId())
                .eq(SysRoleResource::getRootResourceId, app.getId())
                .list()
                .stream().map(SysRoleResource::getRoleId).collect(Collectors.toList());
        if (roleIdList.isEmpty()) {
            return Collections.emptyList();
        }
        Collection<SysRole> roleList = sysRoleService.listByIds(roleIdList);
        return roleList.stream()
                .filter(role -> role.getState().equals(1))
                .map(this::formatRoleDTO)
                .collect(Collectors.toList());
    }

    @Override
    public RoleDto getRoleByCode(String code) {
        SysRole role = sysRoleService.lambdaQuery().eq(SysRole::getCode, code).one();
        return role == null ? null : formatRoleDTO(role);
    }

//    @Override
//    public List<UserDTO> listUserInDept(Integer deptId, String username) {
//        List<UserDTO> l = new ArrayList<>();
//        List<SysUser> d = sysUserService.listUserInDept(deptId);
//        l.add(JsonUtil.convert(d, UserDTO.class));
//        return l;
//    }

    private ResourceDTO formatResourceDTO(SysResource res) {
        return new ResourceDTO()
                .setId(res.getId())
                .setCode(res.getCode())
                .setName(res.getName())
                .setParentId(res.getParentId())
                .setRootId(res.getRootId())
                .setState(res.getState())
                .setType(res.getType())
                .setUrl(res.getUrl())
                .setAuthentication(res.getAuthentication())
                .setAuthorization(res.getAuthorization());
    }

    private DeptDto formatDeptDTO(DeptVO deptVO) {
        return new DeptDto()
                .setParentId(deptVO.getParentId())
                .setName(deptVO.getName())
                .setExpand(deptVO.getExpand())
                .setCode(deptVO.getCode())
                .setAbbreviation(deptVO.getAbbreviation())
                .setId(deptVO.getId());
    }

    private DeptDto formatDeptDTO(SysDept dept) {
        return new DeptDto()
                .setParentId(dept.getParentId())
                .setName(dept.getName())
                .setExpand(null)
                .setCode(dept.getCode())
                .setAbbreviation(dept.getAbbreviation())
                .setId(dept.getId());
    }

    private RoleDto formatRoleDTO(SysRole role) {
        return new RoleDto().setCode(role.getCode()).setId(role.getId()).setName(role.getName());
    }

    private UserDTO formatUserDTO(SysUser user) {
        return new UserDTO().setId(user.getId())
                .setType(user.getType())
                .setPhone(user.getPhone())
                .setMail(user.getMail())
                .setLoginName(user.getLoginName())
                .setProfile(user.getProfile())
                .setName(user.getName())
                .setGender(user.getGender())
                .setIdCard(user.getIdCard())
                .setState(user.getState())
                .setImgUrl(user.getImgUrl())
                .setLastLoginTime(user.getLastLoginTime())
                .setLastLoginIp(user.getLastLoginIp())
                .setEffectTime(user.getEffectTime())
                .setExpireTime(user.getExpireTime())
                .setCreateTime(user.getCreateTime())
                .setUpdateTime(user.getUpdateTime());
    }
}
